﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;

namespace Automata
{
    public class Automata
    {
        private int numeroEstados;
        private int estadoInicial;
        private bool[] estadosAceptacion;
        private ArrayList alfabeto;
        private Dictionary<char, int>[] funcion;

        /// <summary>
        /// Construye el autómata, si hay algún error, se detiene y manda el mensaje de error
        /// </summary>
        /// <param name="numeroEstados">El número de estados</param>
        /// <param name="alfabeto">Los caracteres del alfabeto (sin espacios entre ellos)</param>
        /// <param name="estadoInicial">El estado inicial</param>
        /// <param name="estadosAceptacion">Los estados de aceptación separados por espacio</param>
        /// <param name="funcion">La función de transición, una instruccion por línea del modo: # E #</param>
        /// <param name="stringEvaluar">El string a probar si es aceptado o no</param>
        /// <returns>Un string dando el resultado de la evaluación</returns>
        private string construye(string numeroEstados,
                                 string alfabeto,
                                 string estadoInicial,
                                 string estadosAceptacion,
                                 string funcion)
        {
            //Número estados
            try
            {
                this.numeroEstados = Int32.Parse(numeroEstados);
                if (this.numeroEstados < 1)
                    throw new Exception();
            }
            catch (Exception e)
            {
                return "El número de estados debe ser un entero positivo.";
            }

            //Alfabeto
            this.alfabeto = new ArrayList();
            char[] arreglo = alfabeto.ToCharArray();

            for (int i = 0; i < arreglo.Length; i++)
                if (!this.alfabeto.Contains(arreglo[i]) && arreglo[i] != ' ')
                    this.alfabeto.Add(arreglo[i]);

            if (this.alfabeto.Count == 0)
                return "Escriba los caracteres del alfabeto.";

            //Estado inicial
            try
            {
                this.estadoInicial = Int32.Parse(estadoInicial);
                if (this.estadoInicial < 1 || this.estadoInicial > this.numeroEstados)
                    throw new Exception();
            }
            catch (Exception e)
            {
                return "El estado inicial debe de estar entre 1 y " + this.numeroEstados;
            }

            //Estados aceptación
            this.estadosAceptacion = new bool[this.numeroEstados + 1];

            string[] elementos = estadosAceptacion.Split(new char[] { ' ' });
            int total = 0;

            for (int i = 0; i < elementos.Length; i++)
            {
                if (elementos[i].Length == 0)
                    continue;

                try
                {
                    int temp;
                    temp = Int32.Parse(elementos[i]);

                    if (temp < 1 || temp > this.numeroEstados)
                        throw new Exception();

                    this.estadosAceptacion[temp] = true;
                    total++;
                }
                catch (Exception e)
                {
                    return "Los estados de aceptación deben de estar entre 1 y " + this.numeroEstados;
                }

            }

            if (total == 0)
                return "Escriba estados de aceptación";

            //Funcion de transicion
            this.funcion = new Dictionary<char, int>[this.numeroEstados + 1];
            for (int i = 0; i < this.funcion.Length; i++)
                this.funcion[i] = new Dictionary<char, int>();

            string[] lineas = funcion.Split(new char[] { '\n' });
            total = 0;

            for (int i = 0; i < lineas.Length; i++)
            {
                if (lineas[i].Length == 0)
                    continue;

                try
                {
                    elementos = lineas[i].Split(new char[] { ' ' });

                    int t1, t2, a;
                    char elementoAlfabeto;
                    int indice = -1;

                    //Primer número

                    while (elementos[++indice].Length == 0) ;

                    t1 = Int32.Parse(elementos[indice]);

                    //Caracter

                    while (elementos[++indice].Length == 0) ;

                    if (elementos[indice].Length != 1)
                        throw new Exception();

                    elementoAlfabeto = elementos[indice].ToCharArray()[0];
                    a = this.alfabeto.IndexOf(elementoAlfabeto);

                    if (a == -1)
                        return "En la función de transición sólo se puden usar caracteres del alfabeto.";

                    //Segundo número

                    while (elementos[++indice].Length == 0) ;

                    t2 = Int32.Parse(elementos[indice]);

                    this.funcion[t1].Add(elementoAlfabeto, t2);

                    total++;
                }
                catch (Exception e)
                {
                    return "Hay un error en la sintaxis de la función de transición.";
                }

            }

            if (total == 0)
                return "Escriba elementos de la función de transición";

            return "";
        }

        /// <summary>
        /// Crea un autómata con lo especificado como parámetro validando antes los datos.
        /// Después verifica si el autómata acepta o no el string mandado como parámetro
        /// </summary>
        /// <param name="numeroEstados">El número de estados</param>
        /// <param name="alfabeto">Los caracteres del alfabeto (sin espacios entre ellos)</param>
        /// <param name="estadoInicial">El estado inicial</param>
        /// <param name="estadosAceptacion">Los estados de aceptación separados por espacio</param>
        /// <param name="funcion">La función de transición, una instruccion por línea del modo: # E #</param>
        /// <param name="stringEvaluar">El string a probar si es aceptado o no</param>
        /// <returns>Un string dando el resultado de la evaluación</returns>
        public string evalua(string numeroEstados,
                             string alfabeto,
                             string estadoInicial,
                             string estadosAceptacion,
                             string funcion,
                             string stringEvaluar)
        {
            string temp = construye(numeroEstados, alfabeto, estadoInicial, estadosAceptacion, funcion);
            string resultStr = "- - - - - - - - - - - - - - - - - - - - - - - - - - -" + "<br /><br />";

            if (temp.Length > 0)
                return temp;

            int estadoActual = this.estadoInicial;

            string tempStr = "";
            bool validState;
            int counter = 1;
            foreach (char element in stringEvaluar)
            {
                tempStr = counter + ") " + estadoActual.ToString() + " | " + element + " -- > ";
                validState = this.funcion[estadoActual].TryGetValue(element, out estadoActual);
                if (!validState)
                {
                    tempStr += "El caracter " + element + " no se reconoce";
                    return "El automata <span id='rejectStr'>no acepta</span> el string <br />" + resultStr + tempStr;
                }
                tempStr += estadoActual;
                resultStr += tempStr + "<br />";
                counter++;
            }

            if (this.estadosAceptacion[estadoActual])
            {
                return "El automata <span id='acceptStr'>acepta</span> el string <br />" + resultStr + "<br /> El estado " + estadoActual + " pertenece a los estados de aceptación";
            }

            return "El automata <span id='rejectStr'>no acepta</span> el string <br />" + resultStr + "<br /> El estado " + estadoActual + " no pertenece a los estados de aceptación";

        }
    }
}
